using System;
using System.Collections.Generic;
using System.Collections;

namespace Jonathan.SharpDB
{
    /// <summary>
    /// A table for the SharpDB...
    /// </summary>
    public class Table
    {
        #region - Variables -

        /// <summary>
        /// Holds the Name of the Table...
        /// </summary>
        private string m_Name;

        /// <summary>
        /// Collection of Columns...
        /// </summary>
        private List<Column> m_Cols;

        /// <summary>
        /// Collection of Rows...
        /// </summary>
        private List<ArrayList> m_Rows;

        /// <summary>
        /// Status of the Table...
        /// </summary>
        private TableStatus m_Status;

        /// <summary>
        /// The Database Holding the Table..
        /// </summary>
        private Database m_Database;

        #endregion

        #region - Constructor -

        /// <summary>
        /// Create a new Table with the Given Name...
        /// </summary>
        /// <param name="name">Name of Table.</param>
        /// <param name="db">Database Table is in.</param>
        public Table(string name, Database db)
        {
            this.m_Database = db;
            this.m_Name = name;
            this.m_Cols = new List<Column>();
            this.m_Rows = new List<ArrayList>();
            this.m_Status = TableStatus.DATA_NOT_READ;
        }

        #endregion

        #region - Properties -

        /// <summary>
        /// Gets or Sets the Name of the Table...
        /// </summary>
        public string Name
        {
            get { return this.m_Name; }
            set { this.m_Name = value; }
        }

        /// <summary>
        /// Gets or Sets the Status of the Table...
        /// </summary>
        internal TableStatus Status
        {
            get { return this.m_Status; }
            set { this.m_Status = value; }
        }

        /// <summary>
        /// Gets or Sets the List of Columns...
        /// </summary>
        internal List<Column> Columns
        {
            get { return this.m_Cols; }
			set { this.m_Cols = value; }
        }

        /// <summary>
        /// Gets the List of Rows...
        /// </summary>
        internal List<ArrayList> Rows
        {
            get { return this.m_Rows; }
        }

        #endregion

        #region - Add Column -

        /// <summary>
        /// Add a column with the given Name...
        /// </summary>
        /// <param name="columnName">Name of Column.</param>
        /// <returns>DBMessage</returns>
        internal DBMessage AddColumn(string columnName)
        {
            return this.AddColumn(columnName, ColumnType.STRING);
        }

        /// <summary>
        /// Add a column with the given Name and Type...
        /// </summary>
        /// <param name="columnName">Name of Column.</param>
        /// <param name="columnType">Type of Column.</param>
        /// <returns>DBMessage</returns>
        internal DBMessage AddColumn(string columnName, ColumnType columnType)
        {
            // Look for a column with the same name...
            foreach (Column col in this.m_Cols)
            {
                if (col.ColumnName.Equals(columnName, StringComparison.CurrentCultureIgnoreCase))
                {
                    return DBMessage.COLUMN_EXISTS;
                }
            }

            // Change Status of Table...
            this.m_Status = TableStatus.DATA_CHANGED;

            // Create and add a new column...
			this.m_Cols.Add(new Column(columnName.Trim(), columnType));

            return DBMessage.SUCCESS;
        }

        #endregion

        #region - Add Row -

        internal DBMessage AddRow(ArrayList rowItems)
        {
            // Check to see if the row has the same number of columns...
            if (rowItems.Count != this.m_Cols.Count)
            {
                return DBMessage.COLUMN_ROW_NUMBER_MISMATCH;
            }

            // Change Status of Table...
            this.m_Status = TableStatus.DATA_CHANGED;

            // Add Row...
            this.m_Rows.Add(rowItems);

            return DBMessage.SUCCESS;
        }

        #endregion

        #region - Remove Rows -

        /// <summary>
        /// Removes rows from the table with the given conditions...
        /// </summary>
        /// <param name="query">Query that started the remove.</param>
        /// <returns>Number of Rows Affected.</returns>
        internal int RemoveRow(Query query)
        {
            int cnt = 0;
            if (query.Columns.Count == 0)
            {
                cnt = this.m_Rows.Count;
                // Remove all rows...
                this.m_Rows.Clear();
                return cnt;
            }

            for (int a = this.m_Rows.Count - 1; a >= 0; a--)
            {
                // If we found the same amount of matches as requested, remove the row...
                if (Common.CheckRowMatch(this.m_Rows[a], this.m_Cols, query.Columns, query.Operators, query.Values))
                {
                    this.m_Rows.RemoveAt(a);
                    cnt++;
                }
            }

            return cnt;
        }

        #endregion

        #region - Get Column Index -

        /// <summary>
        /// Gets the Index of a Column...
        /// </summary>
        /// <param name="colName">Name of Column.</param>
        /// <returns>Index of Column.</returns>
        private int GetColumnIndex(string colName)
        {
            for (int a = 0; a < this.m_Cols.Count; a++)
            {
                Column col = (Column)this.m_Cols[a];
                if(colName.Equals(col.ColumnName, StringComparison.CurrentCultureIgnoreCase))
                {
                    return a;
                }
            }

            return -1;
        }

        #endregion

        #region - Update -

        /// <summary>
        /// Runs and Updata query...
        /// </summary>
        /// <param name="query">Update Query</param>
        /// <returns>Affected Rows.</returns>
        internal int UpdateRow(Query query)
        {
            int rows = 0;
            if (query.Columns.Count == 0)
            {
                for (int a = 0; a < this.m_Rows.Count; a++)
                {
                    this.UpdateRows(a, query.ModColumns,query.ModValues);
                    rows++;
                }
                return rows;
            }

            for (int a = 0; a < this.m_Rows.Count; a++)
            {
                // If we found the same amount of matches as requested, update the row...
                if (Common.CheckRowMatch(this.m_Rows[a], this.m_Cols, query.Columns, query.Operators, query.Values))
                {
                    this.UpdateRows(a, query.ModColumns, query.ModValues);
                    rows++;
                }
            }

            return rows;
        }

        /// <summary>
        /// Updates a row...
        /// </summary>
        /// <param name="index">Index of Row</param>
        /// <param name="cols">Columns to Update.</param>
        /// <param name="vals">Values to Update with.</param>
        private void UpdateRows(int index, List<string> cols, ArrayList vals)
        {
            ArrayList row = this.m_Rows[index];
            for (int a = 0; a < cols.Count; a++)
            {
                int colIndex = this.GetColumnIndex(cols[a]);
                if (colIndex != -1)
                {
                    row[colIndex] = vals[a];
                }
            }
        }

        #endregion

        #region - Select -

        /// <summary>
        /// Runs a Select Query...
        /// </summary>
        /// <param name="query">Select Query</param>
        /// <returns>DBMessage</returns>
        internal DBMessage SelectRows(Query query)
        {
            Results r = new Results();

            ArrayList cols = new ArrayList();

            if (query.ModColumns.Count == 1)
            {
                if (query.ModColumns[0].ToString() == "*")
                {
                    for (int z = 0; z < this.m_Cols.Count; z++)
                    {
                        Column col = this.m_Cols[z] as Column;
                        cols.Add(col.ColumnName);
                        r.AddColumn(col.ColumnName, col.ColumnType);
                    }
                }
                else
                {
                    for (int z = 0; z < query.ModColumns.Count; z++)
                    {
                        cols.Add(query.ModColumns[z]);
                        Column col = this.m_Cols[this.GetColumnIndex(query.ModColumns[z].ToString())] as Column;
                        r.AddColumn(col.ColumnName, col.ColumnType);
                    }
                }
            }
            else
            {
                for (int z = 0; z < query.ModColumns.Count; z++)
                {
                    cols.Add(query.ModColumns[z]);
                    Column col = this.m_Cols[this.GetColumnIndex(query.ModColumns[z].ToString())] as Column;
                    r.AddColumn(col.ColumnName, col.ColumnType);
                }
            }

            for (int a = 0; a < this.m_Rows.Count; a++)
            {
                if (Common.CheckRowMatch(this.m_Rows[a], this.m_Cols, query.Columns, query.Operators, query.Values))
                {
                    ArrayList row = new ArrayList();
                    for (int b = 0; b < cols.Count; b++)
                    {
                        int colIndex = this.GetColumnIndex(cols[b].ToString());
                        if (colIndex != -1)
                        {
                            ArrayList rowFound = this.m_Rows[a];
                            row.Add(rowFound[colIndex]);
                        }
                    }

                    // Trigger event...
                    this.m_Database.TriggerNewResult(row);
                    r.AddRow(row);
                }
            }

            // Sort Results...
            if (query.OrderByCol != "")
            {
                r.Sort(query.OrderByCol, query.OrderDir);
            }

            // Group Results...

            query.SetResults(r);

            return DBMessage.SUCCESS;
        }

        #endregion

		public void Clear ()
		{
			this.m_Cols.Clear();
			this.m_Rows.Clear();
		}
    }
}
